<?php

declare(strict_types=1);
namespace App\Repository;


use App\Enum\ConstantEnum;
use App\Utils\Cache\RedisKey;
use App\Utils\Cache\RedisUtil;
use Hyperf\Database\Schema\Schema;

/**
 * Class AbstractRepository
 * User: xuxiaoyang
 * DateTime: 2021/12/9 下午5:39
 *
 * @package App\Repository
 */
abstract class AbstractRepository
{
    protected int $page = 1;

    protected int $per_page = 15;

    private $with = "";

    private $join = [];

    private $joinType = "inner";

    private $joinList = [];


    private array $groupBy = [];

    public array $defaultNameList = [
        'create_user',
        'update_user',
        'super_create_user',
        'super_update_user',
    ];

    protected array $appendNameList = [
    ];


    //设置page
    public function setPage($params = ['page'=>1, 'per_page'=>20]){
        if(!empty($params['page']) && $params['page']){
            $this->page = $params['page'];
        }else{
            $this->page = 1;
        }
        if(!empty($params['per_page']) && $params['per_page']){
            if($params['per_page'] > 5000){
                //抛出异常
                throw new PageException(PageException::OVER_LIMIT);
            }
            $this->per_page = $params['per_page'];
        }else{
            $this->per_page = 20;
        }
    }

    public function setWith($with){
        $this->with = $with;
    }

    //join 一个
    public function setJoin(array $join,$joinType = "inner"){
        $this->join = $join;
        //只有3个参数
        $this->joinType = $joinType;
    }

    //join 多个 参考setJoin二维数组
    public function setJoinList(array $joinList){
        $this->joinList = $joinList;
    }


    public function setGroupBy($groupBy){
        $this->groupBy = $groupBy;
    }

    public function setAppendNameList(array $appendNameList){
       $this->appendNameList = $appendNameList;
    }


    protected array $addCommonData = [];

    protected array $updateCommonData = [];

    public function getModel()
    {
        $model = $this->model;
        if(!empty($this->with)){
            $model = $model->with($this->with);
            $this->with = "";
        }
        if(!empty($this->groupBy)){
            $model = $model->groupBy($this->groupBy);
            $this->groupBy = [];
        }
        if(!empty($this->join)){
            //单个
            if($this->joinType == "left"){
                $model = call_user_func_array(array($model, "leftJoin"), $this->join);
            }elseif($this->joinType == "right"){
                $model = call_user_func_array(array($model, "rightJoin"), $this->join);
            }else{
                $model = call_user_func_array(array($model, "join"), $this->join);
            }
            $this->join = "";
            $this->joinType = "inner";
        }elseif(!empty($this->joinList)){
            //多个
            foreach($this->joinList as $item){
                $joinType = $item['joinType'] ?? 'left';
                $join = $item['join'];
                if($joinType == "left"){
                    $model = call_user_func_array(array($model, "leftJoin"), $join);
                }elseif($joinType == "right"){
                    $model = call_user_func_array(array($model, "rightJoin"), $join);
                }else{
                    $model = call_user_func_array(array($model, "join"), $join);
                }
            }
            $this->joinList = [];
        }
        return $model;
    }

    /**
     * Notes: 对where处理，兼容tp对
     *
     * @param  array  $where
     * @return \Hyperf\Database\Query\Builder
     */
    public function parseWhere(array $where = [],$model = null)
    {
//        var_dump($where);
        $and = [];
        $andOr = [];
        $closure = [];
        $with = [];

        //1.先兼容数组方式
        foreach($where as $key => $value){
            if(is_int($key)){
                $and[] = $value;
                unset($where[$key]);
            }else if($key == "andOr"){
                $andOr = $value;
                unset($where[$key]);
            }else if($key == "closure"){
                $closure = $value;
                unset($where[$key]);
            }else if(is_array($value)) {
                if (isset($value[0]) && $value[0] == "with") {
                    $with[$key] = $value[1];
                    unset($where[$key]);
                }
            }

        }
        $model = is_null($model) ? $this->getModel() : $model;
        $isTp = true;
        if($and){
            $model = $model->where($and);
            $isTp = false;
        }
        if($andOr){
            if(is_array($andOr)){
                foreach($andOr as $value){
                    $model = $model->where($value);
                }
            }else{
                $model = $model->where($andOr);
            }
            $isTp = false;
        }
        if($closure){
            if(is_array($closure)){
                foreach($closure as $value){
                    $model = $model->where($value);
                }
            }else{
                $model = $model->where($closure);
            }
            $isTp = false;
        }
        if($with){
            foreach($with as $column => $value){
                $model = $model->with([$column=>$value]);
            }
            $isTp = false;
        }
        //2.条件走tp模式
        if($where){
            $rule = "and";
            if(isset($where['_logic'])){
                //小写处理
                $rule = strtolower($where['_logic']);
                unset($where['_logic']);
            }
            if($isTp){
                $model = $this->parseThinkphpWhere($model,$where,$rule);
            }else{
                $model = $model->where(function ($query) use($where,$rule){
                    $query = $this->parseThinkphpWhere($query,$where,$rule);
                });
            }
        }
        return $model;
    }

    private function parseThinkphpWhere($model,$where,$rule = "and"){
//        var_dump($rule);
        $in = [];
        $common = [];
        //或条件集合
        $single = [];
        $between = [];
        $findInSets = [];
        $notIn = [];
        $findInSetOr = [];
        //复合条件
        $complex = [];
        if(isset($where['_complex'])){
            $complex =$where['_complex'];
            unset($where['_complex']);
        }

        foreach($where as $key => $value){
            if(is_array($value)){
                $singleRule = in_array('or',$value) ? "or" : "and";
                $index = array_search($singleRule,$value);
                if($index !== false){
                    unset($value[$index]);
                }
                //判断是否二维数组
                //'status' => [ [ 'eq' , 1] , [ 'eq' , 2 ] , [ 'eq' , 3 ] , 'or' ] ，默认走and
                $isTwoDimensionalArray = Arr::isTwoDimensionalArray($value);
                if($isTwoDimensionalArray){
                    // 对一个字段使用多个查询条件
                    //特定范围查询  $where['create_time'] = "二维数组条件"；
                    foreach($value as $arr){
                        $item= array_merge([$key],$arr);
                        //默认走普通模式，in之类的todo
                        $single[$singleRule][] = $item;
                    }
                }else{
                    //数组
                    $item= array_merge([$key],$value);
                    if($value[0] == "in"){
                        $in[$key] = $value[1];
                    }else if($value[0] == "between"){
                        $between[$key] =  $value[1];
                    }else if($value[0] == "find_in_set"){
                        $findInSets[$key] =  $value[1];
                    }else if($value[0] == "find_in_set_or"){
                        $findInSetOr[$key] =  $value[1];
                    }else if($value[0] == "not_in"){
                        $notIn[$key] =  $value[1];
                    }else{
                        $common[] = $item;
                    }
                }
            }else{
                //非数组
                $item = [$key,"=",$value];
                $common[] = $item;
            }
        }
        if($common){
            foreach($common as $item){
                list($column,$exp,$value) = $item;
                if($rule == 'and'){
                    $model = $model->where($column,$exp,$value);
                }else{
                    $model = $model->orWhere($column,$exp,$value);
                }
            }
        }

        //如果有where别的条件可以扩展
        if($single){
            //单字段处理
            foreach($single as $singleRule => $whereList){
                if($rule == 'and') {
                    $model = $model->where(function ($query) use ($singleRule, $whereList) {
                        foreach ($whereList as $item) {
                            list($column, $exp, $value) = $item;
                            if ($singleRule == 'and') {
                                // exp默认走where,其他todo
                                if($exp == "in"){
                                    $query = $query->whereIn($column, $value);
                                }else{
                                    $query = $query->where($column, $exp, $value);
                                }
                            } else {
                                if($exp == "in"){
                                    $query = $query->orWhereIn($column, $value);
                                }else if ($exp == 'exp') {
                                    $query = $query->orWhereRaw($value);
                                }else{
                                    $query = $query->orWhere($column, $exp, $value);
                                }
                            }
                        }
                    });
                }else{
                    $model = $model->orWhere(function ($query) use ($singleRule, $whereList) {
                        foreach ($whereList as $item) {
                            list($column, $exp, $value) = $item;
                            if ($singleRule == 'and') {
                                // exp默认走where,其他todo
                                if($exp == "in"){
                                    $query = $query->whereIn($column, $value);
                                }else{
                                    $query = $query->where($column, $exp, $value);
                                }
                            } else {
                                if($exp == "in"){
                                    $query = $query->orWhereIn($column, $value);
                                }else if ($exp == 'exp') {
                                    $query = $query->orWhereRaw($value);
                                }else{
                                    $query = $query->orWhere($column, $exp, $value);
                                }
                            }
                        }
                    });
                }
            }

        }
        if($in){
//            var_dump("in");
//            var_dump($in);
            foreach($in as $column => $value){
                if ($rule == 'and') {
                    $model = $model->whereIn($column,$value);
                }else{
                    $model = $model->orWhereIn($column,$value);
                }
            }
        }
        if($between){
//            var_dump("between");
//            var_dump($between);
            foreach($between as $column => $value){
                if ($rule == 'and') {
                    $model = $model->whereBetween($column,$value);
                }else{
                    $model = $model->orWhereBetween($column,$value);
                }
            }
        }
        if($findInSets){
//            var_dump("find_in_set");
//            var_dump($findInSets);
            if($rule == 'and') {
                foreach($findInSets as $column => $value){
                    $model = $model->where(function ($query) use ($column,$value) {
                        if(is_string($value)){
                            $valueList = explode(',',$value);
                        }else{
                            $valueList = [$value];
                        }
                        foreach($valueList as $val){
                            $query = $query->whereRaw("FIND_IN_SET(?,{$column})",[$val]);
                        }
                    });
                }
            }else{
                foreach($findInSets as $column => $value){
                    $model = $model->orWhere(function ($query) use ($column,$value) {
                        if(is_string($value)){
                            $valueList = explode(',',$value);
                        }else{
                            $valueList = [$value];
                        }
                        foreach($valueList as $val){
                            $query = $query->whereRaw("FIND_IN_SET(?,{$column})",[$val]);
                        }
                    });
                }

            }
        }
        // 单字段多个可能值
        if($findInSetOr) {
            if($rule == 'and') {
                foreach($findInSetOr as $column => $value){
                    $model = $model->where(function ($query) use ($column,$value) {
                        // 数组 或者逗号分隔字符串
                        $valueArr = is_array($value) ? $value : explode(',', $value);
                        $valueArr = array_unique($valueArr);
                        if($valueArr){
                            $left = [];
                            $left = array_pad($left, count($valueArr), "FIND_IN_SET(?,{$column}) > 0");
                            $query = $query->whereRaw("(".implode(" or ", $left).")", $valueArr);
                        }
                    });
                }
            }else{
                foreach($findInSetOr as $column => $value){
                    $model = $model->orWhere(function ($query) use ($column,$value) {
                        // 数组 或者逗号分隔字符串
                        $valueArr = is_array($value) ? $value : explode(',', $value);
                        $valueArr = array_unique($valueArr);
                        if($valueArr){
                            $left = [];
                            $left = array_pad($left, count($valueArr), "FIND_IN_SET(?,{$column}) > 0");
                            $query = $query->whereRaw("(".implode(" or ", $left).")", $valueArr);
                        }
                    });
                }
            }
        }
        if($notIn){
            foreach($notIn as $column => $value){
                if ($rule == 'and') {
                    $model = $model->whereNotIn($column,$value);
                }else{
                    $model = $model->orWhereNotIn($column,$value);
                }
            }
        }
        if($complex){
            $complexRule = "and";
            if(isset($complex['_logic'])){
                //小写处理
                $complexRule = strtolower($complex['_logic']);
                unset($complex['_logic']);
            }
            if($rule == 'and') {
                $model = $model->where(function ($query) use ($complex, $complexRule) {
                    $query = $this->parseThinkphpWhere($query, $complex, $complexRule);
                });
            }else{
                $model = $model->orWhere(function ($query) use ($complex, $complexRule) {
                    $query = $this->parseThinkphpWhere($query, $complex, $complexRule);
                });
            }
        }
        return $model;
    }




    public function insertGetId($data)
    {
        $data = $this->beforeInsert($data);
        return $this->model->insertGetId($data);
    }


    /**
     * 新增
     * User: 汤成
     * Date: 2024/7/07 10:42 上午
     *
     * @param $where
     * @param $data
     */
    public function insert(array $data)
    {
        $data = $this->beforeInsert($data);
        return $this->model->insert($data);
    }

    //一维数组去除不存在的数据
    private function getColumnValue($data,$columns){
        foreach($data as $column => $value){
            if(!in_array($column,$columns)){
                unset($data[$column]);
            }
        }
        return $data;
    }


    public function beforeInsert($data){
        //$this->getAdminInfo();
        //获取表字段
        $columns = $this->getRedisTableColumns($this->model->getTableName());
        $isTwo = false;
        foreach($data as $key => $arr){
            if(is_int($key)){
               $isTwo = true;
            }
        }
        if($isTwo == false){
            if(!empty($this->addCommonData)){
                $data = array_merge($this->addCommonData,$data);
            }
            if(isset($data['org_id'])){
                $data['create_organize'] = $data['org_id'];
            }
            $data = $this->getColumnValue($data,$columns);
        }else{
            foreach($data as $key => $arr){
                if(!empty($this->addCommonData)){
                    $arr = array_merge($this->addCommonData,$arr);
                }
                if(isset($arr['org_id'])){
                    $arr['create_organize'] = $arr['org_id'];
                }
//                var_dump($arr);
                $data[$key] = $this->getColumnValue($arr,$columns);
            }
        }
        return $data;
    }

    private function getAdminInfo(){
        $adminService = new AdminService();
        $admin = $adminService->getAdminInfo(false);
        $time = date("Y-m-d H:i:s");
        if($admin){
            if($admin['scene'] == "super"){
                $addCommonData = [
                    'super_create_user'=> $admin['id'],
                    'super_update_user'=> $admin['id'],
                    'super_created_at' => $time,
                    'super_updated_at' => $time,
                    'created_at' => $time,
                    'updated_at' => $time,
                ];
                $updateCommonData = [
                    'super_update_user' => $admin['id'],
                    'super_updated_at' => $time,
                    'updated_at' => $time,
                ];
            }else{
                $addCommonData = [
                    'create_user'=> $admin['id'],
                    'update_user'=> $admin['id'],
                    'created_at' => $time,
                    'updated_at' => $time,
                    'tenant_id'  => $admin['tenant_id']
                ];
                $updateCommonData = [
                    'update_user' => $admin['id'],
                    'updated_at' => $time,
                ];
            }
        }else{
            //未登录
            $addCommonData = [
                'super_create_user'=> 0,
                'super_update_user'=> 0,
                'super_created_at' => $time,
                'super_updated_at' => $time,
                'create_user'=> 0,
                'update_user'=> 0,
                'created_at' => $time,
                'updated_at' => $time,
            ];
            $updateCommonData = [
                'update_user'=> 0,
                'super_update_user' => 0,
                'super_updated_at' => $time,
                'updated_at' => $time,
            ];
        }
        $this->addCommonData = $addCommonData;
        $this->updateCommonData = $updateCommonData;
    }

    private function getRedisTableColumns($tableName){
        //超级管理员
        $redis = new RedisUtil();
        $redisData = $redis->get(RedisKey::DB_TABLE_COLUMNS,$tableName);
        if(is_null($redisData)){
            $columns = Schema::getColumnListing($tableName);
            if($columns){
                //设置缓存
                $redis->set(RedisKey::DB_TABLE_COLUMNS,json_encode($columns),ConstantEnum::ONE_DAY,$tableName);
                return $columns;
            }
            return [];
        }else{
            return $redisData;
        }
    }


    private function beforeUpdate($data){
        $this->getAdminInfo();
        //获取表字段
        $columns = $this->getRedisTableColumns($this->model->getTableName());
        if(!empty($this->updateCommonData)){
            //array_merge 函数在 PHP 中用于合并两个或多个数组。如果两个数组有相同的字符串索引，后面的数组会覆盖前面的数组的值
            $data = array_merge($this->updateCommonData,$data);
        }
        $data = $this->getColumnValue($data,$columns);
        return $data;
    }

    /**
     * 更新
     * User: 韩硕
     * Date: 2022/1/27 10:42 上午
     *
     * @param $where
     * @param $data
     */
    public function updateByOption($where, $data)
    {
        $data = $this->beforeUpdate($data);
        $model = $this->parseWhere($where);
        return $model->update($data);
    }

    /**
     * Notes: 通过主键修改数量
     * User: xulongbiao
     * DateTime: 2022/2/10 15:56
     *
     * @param $id
     * @param $data
     * @return bool
     */
    public function updateById($id, $data)
    {
        $where = ['id' => $id];
        return $this->updateByOption($where,$data);
    }

    /**
     * Notes: 获取单个字段的值
     * User: tc
     * DateTime: 2024/2/12 19:01
     *
     * @param $where
     * @return \Hyperf\Utils\HigherOrderTapProxy|mixed|void
     */
    public function count($where = [])
    {
        $model = $this->parseWhere($where);
        return $model->count();
    }

    public function sum($where, $column)
    {
        $model = $this->parseWhere($where);
        return $model->sum($column);
    }

    public function exists($where = [])
    {
        $model = $this->parseWhere($where);
        return $model->exists();
    }

    /**
     * Notes: 获取单个字段的值
     * User: xulongbiao
     * DateTime: 2022/2/12 19:01
     *
     * @param $where
     * @param $field
     * @return \Hyperf\Utils\HigherOrderTapProxy|mixed|void
     */
    public function value($where, $field)
    {
        $model = $this->parseWhere($where);
        return $model->value($field);
    }

    public function column($where,$column, $key = null)
    {
        $model = $this->parseWhere($where);
        return $model->pluck($column, $key);
    }


    /**
     * Insert or update a record matching the attributes, and fill it with values.
     *
     * @return bool
     */
    public function updateOrInsert(array $attributes, array $values = [])
    {
        return $this->getModel()->updateOrInsert($attributes, $values);
    }

    public function increment($where, $field, $step)
    {
        return $this->getModel()->where($where)->increment($field, $step);
    }


    /**
     * Notes: 根据ID取单条数据
     * User: xuxiaoyang
     * DateTime: 2021/12/9 下午5:39
     *
     * @param $id
     * @param  bool  $throw
     * @param  array  $columns
     * @return Model|Model[]|\Hyperf\Database\Model\Collection|\Hyperf\Database\Model\Model|null
     */
    public function find($id, $throw = false, $columns = ['*'])
    {

        $model =  $this->getModel();
        $info = $throw ? $model->findOrFail($id, $columns) : $model->find($id, $columns);
        $this->appendField($info);
        return $info;
    }


    /**
     * Notes: 获取第一条数据
     * User: tc
     * DateTime: 2024/12/10 上午10:47
     *
     * @param  array  $where
     * @param  bool  $throw
     * @param  array  $columns
     * @return \Hyperf\Database\Model\Builder|\Hyperf\Database\Model\Model|object|null
     */
    public function first(array $where = [], $throw = false, array $columns = ['*'], $sort = 'id', $orderBy = 'desc')
    {
        $model = $this->parseWhere($where);
        $info =  $throw ? $model->firstOrFail($columns) : $model->first($columns);
        $this->appendField($info);
        return $info;
    }

    /**
     * Notes: 获取列表
     * User: tc
     * DateTime: 2024/12/9 下午5:39
     *
     * @param $where
     * @param  array  $columns
     * @return Model|Model[]|\Hyperf\Database\Model\Collection|\Hyperf\Database\Model\Model|null
     */

    public function get($where = [], $columns = ['*'],$sort = 'id', $orderBy = 'desc')
    {
        $model = $this->parseWhere($where);
        $list =  $model->select($columns)->orderBy($sort, $orderBy)->get();
        foreach ($list as &$value) {
            $this->appendField($value);
        }
        return $list;
    }

    /**
     * Notes: 根据where条件取分页数据
     * User: tc
     * DateTime: 2021/12/20 16:07
     */
    public function paginate($where = [], $columns = ['*'], $sort = 'id', $orderBy = 'desc')
    {
        $model = $this->parseWhere($where);
        //$perPage = null, $columns = [], $pageName = 'page', $page = null
        $page =  $model->orderBy($sort, $orderBy)->paginate($this->per_page,$columns,'page',$this->page);
        foreach ($page->items() as &$value) {
            $this->appendField($value);
        }
        return $page;
    }

    //新增字段
    public function appendField($value){
        if(is_null($value)) return null;
        $valueArray = $value->toArray();

        $adminRepository = new AdminRepository();
        $superRepository = new SuperRepository();

//        foreach($this->defaultNameList as $defaultKey){
//            if(isset($valueArray[$defaultKey])){
//                if($value->{$defaultKey} > 0){
//                    if(strpos($defaultKey,"super_") !== false){
//                        $info = $superRepository->getRedisSuperInfoById($value->{$defaultKey});
//                    }else{
//                        $info = $adminRepository->getRedisAdminInfoById($value->{$defaultKey});
//                    }
//                    $name = isset($info['name']) ? $info['name']: "";
//                    $defaultKeyName = $defaultKey."_name";
//                    $value->{$defaultKeyName} = $name;
//                }
//            }
//        }
        if($this->appendNameList){
            foreach($this->appendNameList as $appendKey){
                if(isset($valueArray[$appendKey])){
                    if($value->{$appendKey} > 0) {
                        if(strpos($appendKey,"super_") !== false){
                            $info = $superRepository->getRedisSuperInfoById($value->{$appendKey});
                        }else{
                            $info = $adminRepository->getRedisAdminInfoById($value->{$appendKey});
                        }
                        $name = isset($info['name']) ? $info['name']: "";
                        $appendKeyName = $appendKey."_name";
                        $value->{$appendKeyName} = $name;
                    }
                }
            }
        }
    }


    /**
     * 通过主键删除记录
     * User: tc
     * Date: 2022/1/26 6:06 下午
     *
     * @param $id
     * @return false|int|mixed
     */
    public function deleteById($id)
    {
        return $this->getModel()->where('id', $id)->delete();
    }

    /**
     * Notes: 非主键条件删除数量
     * User:tc
     * DateTime: 2022/2/10 15:56
     *
     * @param $where
     * @return false|int|mixed
     */
    public function deleteByWhere($where)
    {
        //先执行查询，如果有执行delete
        $model = $this->parseWhere($where);
        $sum = $model->count();
        $limit = 10000;
        if($sum > $limit){
            //分批删除
            $groupNum = ceil($sum / $limit);
//            var_dump($sum,$limit,$groupNum);
            for($i = 0;$i < $groupNum;$i ++){
                $model->limit($limit)->delete();
            }
            return true;
        }else{
            return $model->delete();
        }
    }

    public function findByConditions($conditions)
    {
        $model = $this->parseWhere($conditions);
        $data = $model->get();

        return is_null($data) ? [] : $data->toArray();
    }
    
}
